#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>

#define NUMMBOXES 8
#define MAXFILENAME 64
#define MAXBUFSIZE 128

int main() {
  
  printf("Welcome to our driver test program (vroom, vroom)!\n\n");
  
  printf("This program's PID is %d\n", getpid());
  

  char filename[MAXFILENAME];
  int testNum;
  char xtra;
  int mb = -1; // Mailbox number.
  int fd [NUMMBOXES]; // Mailbox file descriptors.
  int i;
  int numRead;
  char * localBuf, *localBuf2;

  for (i=0; i<NUMMBOXES; i++) { // Initially, process has no mailboxes open.
    fd[i] = -1; 
  }
  while (1) { // Menu-driven test program runs until told to exit.
    printf("\n");
    printf("\t(1) Open a mailbox.\n");
    printf("\t(2) Close a mailbox.\n");
    printf("\t(3) Write to a mailbox.\n");
    printf("\t(4) Read from a mailbox.\n");
    printf("\t(5) Print mailbox status.\n");
    printf("\t(6) Write 1000 characters.\n");
    printf("\t(7) Read 1000 characters.\n");

    printf("\t(0) Exit program.\n");
    printf("Enter the number of the test to execute: ");

    scanf("%d%c", &testNum, &xtra);
    fflush(stdin);

    // Get mailbox number from user if user is opening, closing, reading, or
    //  writing.
    if (testNum == 1 || testNum == 2 || testNum == 3 || testNum == 4
	|| testNum == 6 || testNum == 7 ) {
      printf("Enter a mailbox number: ");
      if (scanf("%d%c", &mb, &xtra) <= 0) {
	fflush(stdin);
	printf("Error: Input is not a number.\n");
	continue;
      }
      if (mb < 0 || mb >= NUMMBOXES) {
	fflush(stdin);
	printf("Error: Mailbox number must be in range [%d, %d].\n", 0, NUMMBOXES);
	continue;
      }
    }

    // Do test requested by user.
    char buf [MAXBUFSIZE];
    int numCharsToRead;
    int fdIndx;
    switch (testNum) {
    case 1: // Open a mailbox.
      sprintf(filename, "%s%d", "/dev/mailbox", mb);
      int tmp = open(filename, O_RDWR);
      if (tmp < 0) {
	perror("Error opening mailbox");
      }
      else {
	fd[mb] = tmp;
      }
      break;
    case 2: // Close a mailbox.
      if (close(fd[mb]) < 0) {
	perror("Error closing mailbox");
      }
      fd[mb] = -1;
      break;
    case 3: // Write to a mailbox.
      printf("Enter a string to write to the mailbox: ");
      fgets(buf, MAXBUFSIZE, stdin); // Get string from user.
      if (write(fd[mb], buf, strlen(buf)-1) < 0) { // Write.
	perror("Error writing to mailbox");
      }
      break;
    case 4: // Read from a mailbox.
      printf("Enter length of string to read from the mailbox: ");
      scanf("%d%c", &numCharsToRead, &xtra); // Get string length from user.
      int numCharsRead = read(fd[mb], buf, numCharsToRead); // Read.
      if (numCharsRead < 0) {
	perror("Error reading from mailbox");
      }
      // Print string read from mailbox.
      printf("Read: <");
      for (i=0; i < numCharsRead; i++) {
	printf("%c", buf[i]);
      }
      printf(">\n");
      break;
    case 5: // Print mailbox status.
      // Can only print if process has a mailbox open.
      fdIndx = -1;
      for (i = 0; i < NUMMBOXES; i++) { // Find a mailbox that process has open
	if (fd[i] >= 0) {               //  (if one exists).
	  fdIndx = i;
	  break;
	}
      }
      ioctl(fd[fdIndx], 0);  // Print.
      break;

    case 6:
      localBuf = malloc(1000 * sizeof(char));
      if ( !localBuf ) { perror("malloc"); exit(1); }
      
      for ( i = 0; i < 1000; i ++ ) {
	if ( !( i % 70) ) {
	  localBuf[i] = '\n';
	}
	else {
	  localBuf[i] = '5';
	}
      }
      localBuf[999] = '\0';

      printf("About to write the following: \n\n%s\n\n",
	     localBuf);
      if ( write( fd[mb], localBuf, 1000 ) < 0 ) {
	perror("Error writing 1000 chars to mb.");
      }
      break;

    case 7:

      localBuf2 = malloc(1000 * sizeof(char));
      if ( !localBuf2 ) { perror("malloc"); exit(1); }
      numRead = read( fd[mb], localBuf2, 1000 );
      if ( numRead < 0 ) {
	perror("Error reading from mailbox");
      }
      else {
	printf("Read %d chars from mailbox\n", numRead );
      }
      printf("Read in: \n\n%s\n\n", localBuf2);

      break;
	
	

    case 0: // Exit.
      printf("Goodbye!\n");
      return 0;
      break;
    default:
      break;
    }
  }
}
